<?php
require 'lib/model/om/BaseApartamentPeer.php';

class ApartamentPeer extends BaseApartamentPeer
{

    protected static $etajNames = array(
        0 => 'Parter',
        -1 => 'Mezanin',
        -2 => 'Demisol',
        -3 => 'Subsol',
        -4 => 'Mansardă',
    );
    protected static $etaje = array(
        0 => 'parter',
        -1 => 'mezanin',
        -2 => 'demisol',
        -3 => 'subsol',
        -4 => 'mansarda',
    );
    protected static $etajOrder = array(
        // cod etaj => ordine
        -3 => 0,
        -2 => 1,
        0 => 2,
        -1 => 3,
        1 => 4, // etajele numerice
        -4 => 5,
    );

    public static function getEtajChoices()
    {
        return self::$etaje;
    }

    /**
     * Returneaza codul numeric al unui etaj
     *
     * @param string|int $etaj Denumirea unui etaj: parter, subsol, 1, 2, 3, ...
     * @return int
     */
    public static function getEtajCode($etaj)
    {
        // a se vedea si EtajValidator->doClean()

        if ($etaj > 0)
        {
            $code = $etaj;
        }
        else
        {
            $code = array_search($etaj, self::$etaje);
        }

        return (int) $code;
    }

    /**
     * Returneaza denumirea unui etaj
     *
     * @param int $etaj Codul numeric al etajului
     * @return string
     */
    public static function getEtajName($etaj)
    {
        $etaj = (int) $etaj;
        if ($etaj > 0)
        {
            return 'Etaj ' . $etaj;
        }
        elseif (isset(self::$etajNames[$etaj]))
        {
            return self::$etajNames[$etaj];
        }

        throw new InvalidArgumentException('Etajul specificat este incorect: ' . $etaj);
    }

    /**
     * Genereaza automat o adresa cunoscand scara, etajul si numarul apartamentului
     */
    static public function generateAddress($scara, $etaj, $numar)
    {
        if (!$scara instanceof Scara)
        {
            $scara = ScaraPeer::retrieveByPK($scara);
        }

        $etaj = self::getEtajName($etaj);
        if (is_numeric($etaj))
        {
            $etaj = 'Etaj ' . $etaj;
        }

        $pattern = "%s, Bl. %s, Sc. %s, %s, Ap. %s";
        $address = sprintf($pattern, $scara->getAddress(), $scara->getBloc()->getName(), $scara->getName(), $etaj, $numar);

        $city_id = sfContext::getInstance()->getUser()->getObject()->getCityId();

        return array('address' => $address, 'city_id' => $city_id);
    }

    public static function getTable($scara_id)
    {
        $apartamente = array();

        $criteria = new UserCriteria(__CLASS__);
        $criteria
                ->add(self::SCARA_ID, $scara_id)
                ->addColumns(array(self::ID, self::NUMAR, self::ETAJ, self::PROPRIETAR));

        $stmt = ApartamentPeer::doSelectStmt($criteria);
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
        {
            if (!isset($apartamente[$row['ETAJ']]))
            {
                $apartamente[$row['ETAJ']] = array();
            }

            $apartamente[$row['ETAJ']][] = myTools::arrayFilterKeys($row, array('ID', 'NUMAR', 'PROPRIETAR'));
        }

        // sortez apartamentele dupa etaj
        self::sortByEtaj($apartamente);
        // pt fiecare etaj ap. se sorteaza dupa numar
        foreach ($apartamente as $etaj => $apartament)
        {
            myTools::sortByKey($apartament, 'NUMAR', null, false);
            // inlocuiesc codul etajului cu denumirea acestuia
            unset($apartamente[$etaj]);
            $apartamente[self::getEtajName($etaj)] = $apartament;
        }

        return $apartamente;
    }

    protected static function sortByEtaj(array &$apartamente)
    {
        uksort($apartamente, array(__CLASS__, '_sortByEtaj'));
    }

    /**
     * Determina ordinea a doua etaje.
     *
     * @param int $etaj1
     * @param int $etaj2
     * @return int ($etaj1 - $etaj2)
     */
    protected static function _sortByEtaj($etaj1, $etaj2)
    {
        // ambele etaje sunt numerice
        if ($etaj1 > 0 && $etaj2 > 0)
        {
            return $etaj1 - $etaj2;
        }
        else
        {
            if ($etaj1 > 0)
            {
                $etaj1 = 1;
            }
            if ($etaj2 > 0)
            {
                $etaj2 = 1;
            }

            return self::$etajOrder[$etaj1] - self::$etajOrder[$etaj2];
        }
    }

    /**
     * Returneaza lista cu apartamente din BD, asa cum rezulta dupa interogare (fara nici o alta procesare ulterioara)
     *
     * @param type $date
     * @return PDOStatement
     */
    protected static function getRawApartamente(array $columns = array())
    {
        $criteria = new UserCriteria('BlocPeer');

        $criteria->addColumns($columns);
        $criteria
                ->addJoin(BlocPeer::ID, ScaraPeer::BLOC_ID, Criteria::LEFT_JOIN)
                ->addJoin(ScaraPeer::ID, self::SCARA_ID, Criteria::LEFT_JOIN);

        $stmt = BlocPeer::doSelectStmt($criteria);

        return $stmt;
    }

    /**
     * Creaza o interogare pentru lista cu apartamente din BD
     *
     * @param array $columns
     * @return Criteria
     */
    protected static function getApartamenteCriteria()
    {
        $columns = array(
            'APARTAMENT_ID' => self::ID,
            'BLOC'     => BlocPeer::DENUMIRE,
            'SCARA'    => ScaraPeer::DENUMIRE,
            self::PROPRIETAR,
            ApartamentPeer::ETAJ,
            ApartamentPeer::NUMAR,
        );

        $criteria = new UserCriteria('BlocPeer');

        $criteria
                ->addColumns($columns)
                ->addJoin(BlocPeer::ID, ScaraPeer::BLOC_ID, Criteria::INNER_JOIN)
                ->addJoin(ScaraPeer::ID, self::SCARA_ID, Criteria::INNER_JOIN);

        return $criteria;
    }

    /**
     * Returneaza lista de apartamente pentru toata asociatia,
     * lista structurata dupa blocuri si scari
     *
     * @param string $date (YYYY-MM-DD)
     * @return array
     */
    public static function getApartamente($date = null)
    {
        $columns = array(
            'APARTAMENT_ID' => self::ID,
            'SCARA_ID'      => ScaraPeer::ID,
            'BLOC_ID'       => BlocPeer::ID,
            'BLOC'          => BlocPeer::DENUMIRE,
            'SCARA'         => ScaraPeer::DENUMIRE,
            'ADRESA'        => ScaraPeer::ADRESA,
            self::PROPRIETAR,
            self::ETAJ,
            self::NUMAR,
            ScaraPeer::APARTAMENTE_RAMASE,
        );

        $stmt = self::getRawApartamente($columns, $date);

        $return = array();
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
        {
            $bloc_id = $row['BLOC_ID'];
            $scara_id = $row['SCARA_ID'];
            $apartament_id = $row['APARTAMENT_ID'];

            $apartament = array(
                'etaj'          => self::getEtajName($row['ETAJ']),
                'numar'         => $row['NUMAR'],
                'proprietar'    => $row['PROPRIETAR'],
            );

            $scara = array(
                'nume' => $row['SCARA'],
                'adresa' => $row['ADRESA'],
                'apartamente_ramase' => $row['APARTAMENTE_RAMASE'],
                'apartamente_array' => array($apartament_id => $apartament),
            );

            $bloc = array(
                'nume' => $row['BLOC'],
                'scara_array' => array($scara_id => $scara),
            );

            if (empty($return[$bloc_id]))
            {
                $return[$bloc_id] = $bloc;
            }
            elseif (isset($scara_id) && empty($return[$bloc_id]['scara_array'][$scara_id]))
            {
                $return[$bloc_id]['scara_array'][$scara_id] = $scara;
            }
            elseif (isset($apartament_id) && empty($return[$bloc_id]['scara_array'][$scara_id]['apartamente_array'][$apartament_id]))
            {
                $return[$bloc_id]['scara_array'][$scara_id]['apartamente_array'][$apartament_id] = $apartament;
            }
        }

        // sort on bloc names
        myTools::sortByKey($return, 'nume');
        foreach ($return as $bloc_id => $bloc)
        {
            myTools::sortByKey($return[$bloc_id]['scara_array'], 'nume');
            foreach ($return[$bloc_id]['scara_array'] as $scara_id => $scara)
            {
                myTools::sortByKey($return[$bloc_id]['scara_array'][$scara_id]['apartamente_array'], 'numar');
            }
        }

        return $return;
    }

    /**
     * Returneaza consumurile apartementelor intr-o anumita luna.
     * Rezultatul este de tip array cu 2 elemente:
     *  - apartamente: lista apartamentelor ordonata dupa blocuri, scari, etaje
     *  - consumuri: criteriile asociate si denumirile consumurilor
     *
     * Structura listei cu apartamente este de forma:
     *      bloc
     *          scara
     *              cod etaj
     *                  etaj
     *                      numar apart
     *                          - APARTAMENT_ID
     *                          - PROPRIETAR
     *                          - CONSUMURI array(criteriu_id => valoare consum)
     *
     * @param int $month
     * @return array
     */
    public static function getConsumuriApartamente( $month )
    {
        // lista cu apartamente
        $criteria = self::getApartamenteCriteria();
        $consumuriColumns = array(
            ConsumPeer::CRITERIU_ID,
            ConsumPeer::VALOARE,
        );
        $criteria
                ->addColumns( $consumuriColumns )
                ->addJoin( array(self::ID, $month), array(ConsumPeer::APARTAMENT_ID, ConsumPeer::LUNA), Criteria::LEFT_JOIN );

        $stmt = BlocPeer::doSelectStmt($criteria);

        $apartamente = self::_groupForConsumuri($stmt);
        self::_sort($apartamente);

        // detalii despre consumuri
        $consumuri = ConsumPeer::getSummary();

        // construiesc rezultatul
        $result = array(
            'apartamente'   => $apartamente,
            'consumuri'     => $consumuri,
        );

        return $result;
    }

    public static function getApartamenteForWidget()
    {
        $columns = array(
            'APARTAMENT_ID' => self::ID,
            'BLOC'     => BlocPeer::DENUMIRE,
            'SCARA'    => ScaraPeer::DENUMIRE,
            self::PROPRIETAR,
            ApartamentPeer::ETAJ,
            ApartamentPeer::NUMAR,
        );

        $stmt = self::getRawApartamente($columns);

        $apartamente = array();
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
        {
            $bloc  = $row['BLOC'];
            $scara = $row['SCARA'];
            $etaj  = $row['ETAJ'];
            $etajName = self::getEtajName($row['ETAJ']);

            if (!isset($apartamente[$bloc]))
            {
                $apartamente[$bloc] = array();
            }

            if (!isset($apartamente[$bloc][$scara]))
            {
                $apartamente[$bloc][$scara] = array();
            }

            if (!isset($apartamente[$bloc][$scara][$etaj]))
            {
                $apartamente[$bloc][$scara][$etaj] = array($etajName => array());
            }

            $apartamente[$bloc][$scara][$etaj][$etajName][$row['NUMAR']] = array(
                'APARTAMENT_ID'     => $row['APARTAMENT_ID'],
                'PROPRIETAR'        => $row['PROPRIETAR'],
            );
        }

        // sortare dupa numele blocului
        uksort($apartamente, 'strnatcmp');

        // sortare dupa numele scarii
        foreach ($apartamente as $bloc => $scara)
        {
            uksort($scara, 'strnatcmp');

            // sortare dupa etaj
            foreach ($scara as $key => $etaje)
            {
                self::sortByEtaj($etaje);

                // se elimina codul etajului si se pastreaza doar denumirea acestuia
                $etajNames = array();
                foreach ($etaje as $etajCode => $apEtaj)
                {
                    $apEtajList = current($apEtaj);
                    // sortare apartamente dupa numar
                    uksort($apEtajList, 'strnatcmp');
                    $etajNames[key($apEtaj)] = $apEtajList;
                }

                $scara[$key] = $etajNames;
            }


            $apartamente[$bloc] = $scara;
        }

        return $apartamente;
    }

    protected static function _groupForConsumuri(PDOStatement $stmt)
    {
        $apartamente = array();
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
        {
            $bloc  = $row['BLOC'];
            $scara = $row['SCARA'];
            $etaj  = $row['ETAJ'];
            $etajName = self::getEtajName($row['ETAJ']);

            if (!isset($apartamente[$bloc]))
            {
                $apartamente[$bloc] = array();
            }

            if (!isset($apartamente[$bloc][$scara]))
            {
                $apartamente[$bloc][$scara] = array();
            }

            if (!isset($apartamente[$bloc][$scara][$etaj]))
            {
                $apartamente[$bloc][$scara][$etaj] = array($etajName => array());
            }

            if (!isset($apartamente[$bloc][$scara][$etaj][$etajName][$row['NUMAR']]))
            {
                $apartamente[$bloc][$scara][$etaj][$etajName][$row['NUMAR']] = array(
                    'APARTAMENT_ID'     => $row['APARTAMENT_ID'],
                    'PROPRIETAR'        => $row['PROPRIETAR'],
                    'CONSUMURI'         => array(),
                );
            }

            $apartamente[$bloc][$scara][$etaj][$etajName][$row['NUMAR']]['CONSUMURI'][$row['CRITERIU_ID']] = ConsumPeer::formatValoare($row['VALOARE']);

        }

        return $apartamente;
    }

    protected static function _sort(array &$apartamente)
    {
        // sortare dupa numele blocului
        uksort($apartamente, 'strnatcmp');

        // sortare dupa numele scarii
        foreach ($apartamente as $bloc => $scara)
        {
            uksort($scara, 'strnatcmp');

            // sortare dupa etaj
            foreach ($scara as $key => $etaje)
            {
                self::sortByEtaj($etaje);

                // se elimina codul etajului si se pastreaza doar denumirea acestuia
                $etajNames = array();
                foreach ($etaje as $etajCode => $apEtaj)
                {
                    $apEtajList = current($apEtaj);
                    // sortare apartamente dupa numar
                    uksort($apEtajList, 'strnatcmp');
                    $etajNames[key($apEtaj)] = $apEtajList;
                }

                $scara[$key] = $etajNames;
            }


            $apartamente[$bloc] = $scara;
        }

    }

    /**
     * Verifica daca un apartament este deja inregistrat
     *
     * @param int $numar Numarul apartamentului care se verifica
     * @param int $scara_id Scara din care face parte apartamentul
     * @return bool
     */
    public static function numarExists($numar, $scara_id)
    {
        $criteria = new UserCriteria(get_class());
        $criteria
            ->add(self::NUMAR, $numar)
            ->add(self::SCARA_ID, $scara_id);

        return (bool) self::doCount($criteria);
    }

    public static function getValidationCriteria()
    {
        $criteria = new UserCriteria('BlocPeer');
        $criteria
            ->addJoin(self::SCARA_ID, ScaraPeer::ID, Criteria::INNER_JOIN)
            ->addJoin(ScaraPeer::BLOC_ID, BlocPeer::ID);

        return $criteria;
    }

    public static function validate( $value )
    {
        $criteria = self::getValidationCriteria();
        $criteria->add(self::ID, $value);
        $valid = self::doCount($criteria);

        return $valid;
    }

}
